package pkg

import (
	"context"
	"fmt"
	"gitee.com/ltotal/ppw_gin/plugins/gorm/curd"
	"gitee.com/ltotal/ppw_gin/plugins/gorm/tracer/sky"
	"github.com/SkyAPM/go2sky"
	"github.com/gin-gonic/gin"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"time"
)

type DBPoolConfig struct {
	Type            string `mapstructure:"type"`
	Host            string `mapstructure:"host"`
	Port            int    `mapstructure:"port"`
	User            string `mapstructure:"user"`
	Password        string `mapstructure:"password"`
	Database        string `mapstructure:"db"`
	Charset         string `mapstructure:"charset"`
	TablePrefix     string `mapstructure:"table_prefix"`
	MaxOpenConns    int    `mapstructure:"max_open_conns"`     // 最大连接数
	MaxIdleConns    int    `mapstructure:"max_idle_conns"`     // 最大空闲连接数
	ConnMaxLifetime int    `mapstructure:"conn_max_lifetime"`  // 连接的最大存活时间
	ConnMaxIdleTime int    `mapstructure:"conn_max_idle_time"` // 连接空闲超时
}

type DatabasePkg struct {
}

var Database = &DatabasePkg{}

type Callback func(*gorm.DB)

func (c *DatabasePkg) Init(dbc *DBPoolConfig, callback Callback) {
	db := c.GetDB(dbc)
	if callback != nil {
		callback(db)
	}
}

func (c *DatabasePkg) GetDB(config *DBPoolConfig) *gorm.DB {
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local&multiStatements=true&interpolateParams=true",
		config.User, config.Password, config.Host, config.Port, config.Database, config.Charset)

	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		Logger: &GormLogger{},
	})

	if err != nil {
		panic(fmt.Sprintf("初始化数据库连接失败：%s", err))
	}

	sqlDB, err := db.DB()
	if err != nil {
		panic(fmt.Sprintf("获取 *sql.DB 对象失败：%s", err))
	}
	if config.MaxOpenConns > 0 {
		sqlDB.SetMaxOpenConns(config.MaxOpenConns)
	} else {
		sqlDB.SetMaxOpenConns(32)
	}
	if config.MaxIdleConns > 0 {
		sqlDB.SetMaxIdleConns(config.MaxOpenConns)
	} else {
		sqlDB.SetMaxIdleConns(16)
	}
	if config.ConnMaxLifetime > 0 {
		sqlDB.SetConnMaxLifetime(time.Duration(config.ConnMaxLifetime) * time.Second)
	} else {
		sqlDB.SetConnMaxLifetime(time.Duration(600) * time.Second)
	}
	if config.ConnMaxIdleTime > 0 {
		sqlDB.SetConnMaxIdleTime(time.Duration(config.ConnMaxIdleTime) * time.Second)
	} else {
		sqlDB.SetConnMaxIdleTime(time.Duration(600) * time.Second)
	}

	if err = db.Use(sky.New(
		go2sky.GetGlobalTracer(),
		sky.WithPeerAddr(fmt.Sprintf("%s:%d", config.Host, config.Port)),
		sky.WithSQLDBType(sky.MYSQL),
		sky.WithQueryReport(),
		sky.WithParamReport()),
	); err != nil {
		panic(fmt.Sprintf("gorm SKY插件引用失败：%s", err))
	}

	if err = db.Use(curd.New()); err != nil {
		panic(fmt.Sprintf("gorm CURD插件引用失败：%s", err))
	}

	return db
}

type GormLogger struct {
}

func (p *GormLogger) LogMode(_ logger.LogLevel) logger.Interface {
	return p
}

func (p *GormLogger) Info(_ context.Context, _ string, _ ...interface{}) {
}

func (p *GormLogger) Warn(_ context.Context, _ string, _ ...interface{}) {
}

func (p *GormLogger) Error(_ context.Context, _ string, _ ...interface{}) {
}

func (p *GormLogger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), _ error) {
	if !gin.IsDebugging() {
		return
	}
	sentence, _ := fc()
	SQLStatement := fmt.Sprintf("%s - [%s]", sentence, time.Since(begin))
	fmt.Println(SQLStatement)

	ginCtx, existed := GCM.GetContext(ctx)
	var hSqls []string
	if existed {
		sqls, ok := ginCtx.Get("sqls")
		if ok {
			hSqls = sqls.([]string)
			hSqls = append(hSqls, SQLStatement)
		} else {
			hSqls = []string{SQLStatement}
		}
		ginCtx.Set("sqls", hSqls)
	}
}
